﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ForumServer;
using System.Windows.Forms;
using log4net;
using System.Collections;
using System.Threading;
using ForumServer.Forum;
using ForumLibrary;
using ForumLibrary.Data;
 
namespace ForumGUI
{
    public class ClientController : MarshalByRefObject, IObserver
    {
        private string              _username;
        private ILog                logger;
        private ForumManager       _forumManager;
        private List<IObserver>     _Screens;
        private Stack<Form>         _lastForms;
        private ReaderWriterLock    _notificationsLock;
        private ReaderWriterLock    _lastFormsLock;

        public ClientController(string username, ref ForumManager IforumManager, ref ILog ILogger)
        {
            _username = username;
            _forumManager = IforumManager;
            _Screens = new List<IObserver>();
            _lastForms = new Stack<Form>();
            logger = ILogger;
            _notificationsLock = new ReaderWriterLock();
            _lastFormsLock = new ReaderWriterLock();
            _forumManager.subscribe(this, _username, true);
        }

        public String Username { get { return _username; } }
        public ILog Logger { get { return logger; } }

        /// <summary>
        /// pop the last form used by the user
        /// </summary>
        /// <returns>the last form used by the user</returns>
        public Form popForm() 
        {
            Form lastForm = null;
            _lastFormsLock.AcquireReaderLock(-1);
            try
            {
                logger.Info("Acquired the lock for poping the last form from stack");
                lastForm = _lastForms.Pop();
                lastForm = _lastForms.Peek();
            }
            catch
            {
                logger.Error("Could not pop the last form from stack");
            }
            _lastFormsLock.ReleaseReaderLock();
            logger.Info("Released the lock for after poping the last form from stack");
            return lastForm;
        }
        
        /// <summary>
        /// push the current working form
        /// </summary>
        /// <param name="form">the form to be pushed</param>
        public void pushForm(Form form)
        {
            _lastFormsLock.AcquireWriterLock(-1);
            try
            {
                logger.Info("Acquired the lock for pushing a form into the last form stack");
                _lastForms.Push(form);
            }
            catch
            {
                logger.Error("Could not push form into the last form stack");
            }
            _lastFormsLock.ReleaseWriterLock();
            logger.Info("Released the lock for after pushing a form into the last form stack");
            
        }

        /// <summary>
        /// peek to see what form is the current working form by the user
        /// </summary>
        /// <returns>the current working form by the user</returns>
        public Form peekForm()
        {
            Form lastForm = null;
            _lastFormsLock.AcquireReaderLock(-1);
            try
            {
                logger.Info("Acquired the lock for peeking to the last form stack");
                lastForm = _lastForms.Peek();
            }
            catch
            {
                logger.Error("Could not add peek at the last form stack");
            }
            _lastFormsLock.ReleaseReaderLock();
            logger.Info("Released the lock for after peeking to the last form stack");
            return lastForm;
        }

        /// <summary>
        /// A method executed by the server(Observable).
        /// Updates the client with a message stating that the server's state
        /// has changed and could influence the client's state
        /// </summary>
        /// <param name="messageType">the message type</param>
        /// <param name="username">the user that changed the server's state - optional arument</param>
        /// <param name="forumName">the forum that changed in the server - optional arument</param>
        /// <param name="threadName">the thread that changed in the server - optional arument</param>
        public void update(ForumLibrary.Message messageType, string message, string username = "", string forumName = "", string threadName = "")
        {
            String[] args = { username, forumName, threadName };
            Form lastForm = _lastForms.Peek();
            string notification = message;
            switch (messageType)
            {
                case ForumLibrary.Message.ThreadAdded:
                    try
                    {
                        //Check if I'm viewing the same forum
                        if (lastForm is ThreadsFrame && ((ThreadsFrame)lastForm).ForumName.Equals(forumName))
                        {
                            ((ThreadsFrame)lastForm).update(ForumLibrary.Message.ThreadAdded, notification);
                        }
                    }
                    catch
                    {
                        logger.Error("Could not get friends list or could not update the user with " + messageType + " notification");
                    }                    
                    break;
                case ForumLibrary.Message.ThreadEdited:
                    try
                    {
                        //Check if I'm viewing the same forum
                        if (lastForm is ThreadsFrame && ((ThreadsFrame)lastForm).ForumName.Equals(forumName))
                        {
                            ((ThreadsFrame)lastForm).update(ForumLibrary.Message.ThreadAdded, notification);
                        }

                    }
                    catch
                    {
                        logger.Error("Could not get friends list or could not update the user with " + messageType + " notification");
                    }
                    break;
                case ForumLibrary.Message.PostAdded:
                    try
                    {
                        //Check if I'm viewing the same thread
                        if (lastForm is PostsFrame && ((PostsFrame)lastForm).ThreadName.Equals(threadName)
                            && ((PostsFrame)lastForm).ForumName.Equals(forumName))
                        {
                            ((PostsFrame)lastForm).update();
                        }
                    }
                    catch
                    {
                        logger.Error("Could not get friends list or could not update the user with " + messageType + " notification");
                    }
                    break;
                case ForumLibrary.Message.PostEdited:
                    try
                    {
                        //Check if I'm viewing the same thread
                        if (lastForm is PostsFrame && ((PostsFrame)lastForm).ThreadName.Equals(threadName)
                            && ((PostsFrame)lastForm).ForumName.Equals(forumName))
                        {
                            ((PostsFrame)lastForm).update();
                        }
                    }
                    catch
                    {
                        logger.Error("Could not get friends list or could not update the user with " + messageType + " notification");
                    }
                    break;
                case ForumLibrary.Message.PostDeleted:
                    try
                    {
                        //Check if I'm viewing the same thread
                        if (lastForm is PostsFrame && ((PostsFrame)lastForm).ThreadName.Equals(threadName)
                            && ((PostsFrame)lastForm).ForumName.Equals(forumName))
                        {
                            ((PostsFrame)lastForm).update();
                        }
                    }
                    catch
                    {
                        logger.Error("Could not get friends list or could not update the user with " + messageType + " notification");
                    }
                    break;

                case ForumLibrary.Message.Notification:
                    createNotification(messageType, notification, args);
                    break;
                default:
                    break;
            }
            //_notificationsLock.ReleaseWriterLock();
        }

        public void createNotification(ForumLibrary.Message messageType, string content, string[] arguments)
        {
            Notification notification = new Notification(messageType, content);
            NotificationFrame n = new NotificationFrame(ref _forumManager, this, notification, arguments);
            n.ShowDialog();
        }
        
        /// <summary>
        /// Method to set that the remote object "Client Controller" would be
        /// available indefinitely and not disconnect after 5 minutes
        /// </summary>
        /// <returns>null to set indefinitely time window </returns>
        public override object InitializeLifetimeService()
        {
            //return base.InitializeLifetimeService();
            return null;
        }

        internal string[] getLastNotification()
        {
            List<Notification> nots = this.getNotifications();
            nots.Reverse();
            Notification[] temp = nots.ToArray();
            string[] ret = new string[temp.Length];
            for (int i = 0; i < ret.Length; i++)
                ret[i] = temp[i].NotificationMessage + " (" +temp[i].timeStamp + ")";
            return ret;
        }


        internal List<Notification> getNotifications()
        {
            return this._forumManager.getUserNotifications(this._username);
        }
    }
}
